﻿using LettuceEncrypt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Security.Cryptography.X509Certificates;

namespace Quickly.Certs.Core.LettuceEncrypt
{
    public sealed class DatabaseCertificateRepository : ICertificateRepository, ICertificateSource
    {
        private IOptions<DirectoryCertsPersistenceOptions> _options;
        private IServiceProvider _serviceProvider;
        
        public DatabaseCertificateRepository(
            IOptions<DirectoryCertsPersistenceOptions> options,
            IServiceProvider serviceProvider)
        {
            _options = options;
            _serviceProvider = serviceProvider;
        }

        public async Task<IEnumerable<X509Certificate2>> GetCertificatesAsync(CancellationToken cancellationToken)
        {
            var datas = new List<CertificateData>();

            using (var serviceScope = _serviceProvider.CreateScope())
            {
                var _certificateDataStore = serviceScope.ServiceProvider.GetRequiredService<ICertificateDataStore>();
                var _domainNameStore = serviceScope.ServiceProvider.GetRequiredService<IDomainNameStore>();

                var ids = await _domainNameStore.LoadCertificateIdsAsync(cancellationToken);
                var list = await _certificateDataStore.LoadAsync(ids, cancellationToken);

                datas.AddRange(list);
            }

            var certs = new List<X509Certificate2>();

            foreach (var data in datas)
            {
                var cert = X509CertificateLoader.LoadPkcs12(
                    data: data.Data,
                    password: data.PfxPassword);

                certs.Add(cert);
            }

            return certs.AsEnumerable();
        }

        public async Task SaveAsync(X509Certificate2 certificate, CancellationToken cancellationToken)
        {
            using (var serviceScope = _serviceProvider.CreateScope())
            {
                var _certificateDataStore = serviceScope.ServiceProvider.GetRequiredService<ICertificateDataStore>();
                var _domainNameStore = serviceScope.ServiceProvider.GetRequiredService<IDomainNameStore>();

                var dnsNames = certificate.GetAllDnsNames().Distinct().ToList();    // 去重

                var linkedCertificateIds = await _domainNameStore.GetCertificateIdsAsync(dnsNames, cancellationToken);

                await _certificateDataStore.DeleteAsync(linkedCertificateIds, cancellationToken);

                var data = new CertificateData
                {
                    PfxPassword = _options.Value.PfxPassword,
                    Data = certificate.Export(X509ContentType.Pfx, _options.Value.PfxPassword),
                    NotAfter = certificate.NotAfter,
                };

                var certId = await _certificateDataStore.SaveAsync(data, cancellationToken);

                await _domainNameStore.SaveAsync(dnsNames, certId, cancellationToken);
            }
        }
    }
}
